#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <poll.h>
#include <err.h>

#include <xcb/xcb.h>
#include <xcb/xcb_event.h>


void handle_event(xcb_generic_event_t *evt)
{
	switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
		case XCB_MAP_REQUEST:
			printf("map request\n");
			break;
		case XCB_DESTROY_NOTIFY:
			printf("destroy notify\n");
			break;
		case XCB_UNMAP_NOTIFY:
			printf("unmap notify\n");
			break;
		case XCB_CLIENT_MESSAGE:
			printf("client message\n");
			break;
		case XCB_CONFIGURE_REQUEST:
			printf("config request\n");
			break;
		case XCB_CONFIGURE_NOTIFY:
			printf("config notify\n");
			break;
		case XCB_PROPERTY_NOTIFY:
			printf("property notify\n");
			break;
		case XCB_ENTER_NOTIFY:
			printf("enter notify\n");
			break;
		case XCB_MOTION_NOTIFY:
			printf("motion notify\n");
			break;
		case XCB_BUTTON_PRESS:
			printf("button\n");
			break;
		case XCB_FOCUS_IN:
			printf("focus in\n");
			break;
		case XCB_MAPPING_NOTIFY:
			printf("mapping notify\n");
			break;
		case 0:
			// process_error(evt);
			printf("error\n");
			break;
		default:
			printf("default\n");
			break;
	}
}

bool check_connection(xcb_connection_t *connection)
{
	int xerr;
	if ((xerr = xcb_connection_has_error(connection)) != 0) {
		warn("The server closed the connection: ");
		switch (xerr) {
			case XCB_CONN_ERROR:
				warn("socket, pipe or stream error.\n");
				break;
			case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
				warn("unsupported extension.\n");
				break;
			case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
				warn("not enough memory.\n");
				break;
			case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
				warn("request length exceeded.\n");
				break;
			case XCB_CONN_CLOSED_PARSE_ERR:
				warn("can't parse display string.\n");
				break;
			case XCB_CONN_CLOSED_INVALID_SCREEN:
				warn("invalid screen.\n");
				break;
			case XCB_CONN_CLOSED_FDPASSING_FAILED:
				warn("failed to pass FD.\n");
				break;
			default:
				warn("unknown error.\n");
				break;
		}
		return false;
	} else {
		return true;
	}
}





int main()
{
	int default_screen;
	xcb_connection_t *connection;

	connection = xcb_connect(NULL, &default_screen);

	//xcb_connection_has_error();
	if(!check_connection(connection)) {
		exit(EXIT_FAILURE);
	}

	// get screen
	xcb_screen_t *screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;


	// create window
	xcb_drawable_t  window     = screen->root;
	xcb_gcontext_t  foreground = xcb_generate_id (connection);
	uint32_t        mask       = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
	uint32_t        values[2]  = {screen->black_pixel, 0};
	//	graphic context for drawing
	xcb_create_gc(connection, foreground, window, mask, values);
	//  ask for a graphic context ID
	window = xcb_generate_id (connection);

	mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
	values[0] = screen->white_pixel;
	values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
		XCB_EVENT_MASK_BUTTON_RELEASE ;

	xcb_create_window (connection,                    /* connection          */
			XCB_COPY_FROM_PARENT,          /* depth               */
			window,                        /* window Id           */
			screen->root,                  /* parent window       */
			0, 0,                          /* x, y                */
			150, 150,                      /* width, height       */
			10,                            /* border_width        */
			XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class               */
			screen->root_visual,           /* visual              */
			mask, values );                /* masks */

	xcb_map_window (connection, window);


	int connection_fd = xcb_get_file_descriptor(connection);

	bool running = true;

	struct pollfd fds[1];

	fds[0].fd = connection_fd;
	fds[0].events = POLLIN;

	xcb_generic_event_t *event;
	while(running) {
		xcb_flush(connection);

		// TODO: polling ...
		if(poll(fds, 1, -1) > 0) {
			if(fds[0].revents & POLLIN) {
				while ((event = xcb_poll_for_event(connection)) != NULL) {
					handle_event(event);
					// free(event);
				}
			}
		}

		if (!check_connection(connection)) {
			running = false;
		}
	}

	// TODO: cleanup();
}

// NOTE: xcb_poly_fill_rectangle
